import os

from typing import Union, List, Tuple

from script.server.teleweb.TelewebServer import TelewebServer
from script.util.Print import Print
from script.util.ProgressBar import ProgressBar


class TelewebDownloader(object):
    CACHE_INVALID = 0x0000
    CACHE_DOWNLOADED = 0x0001
    CACHE_RENAMED = 0x0002
    CACHE_SPARSE_FIXED = 0x0004
    CACHE_PROG_CHECKED = 0x0008

    DATA_PATH = os.path.abspath('%s/../cache' % os.path.dirname(os.path.abspath(__file__)))

    def __init__(self, server: TelewebServer):
        self.mServer = server

    def cd(self, _path: str) -> bool:
        path: Union[str, int] = _path
        if path.startswith('#'):
            path = int(path[1:])
        else:
            path = path.replace('\\', '/')

        return self.mServer.cd(path)

    def ls(self) -> List[Tuple[str, str, str, str]]:
        return self.mServer.ls_info()

    def ls_filename(self) -> List[str]:
        return [i[0] for i in self.ls()]

    def pwd(self) -> str:
        return self.mServer.pwd()

    def is_sw_like(self) -> bool:
        return self.mServer.is_sw_like()

    def download(self, download_sub_dir: bool = False) -> Union[str, None]:
        # if not self.mServer.is_sw_like():
        #     return None

        # prepare directory
        sw_path = '%s/%s' % (self.DATA_PATH, self.mServer.pwd().strip('[ /]'))
        sw_path = os.path.abspath(sw_path)

        cache_status: int = TelewebDownloader.get_cache_status(sw_path)

        # download software
        cache_status = self.download_sw(sw_path, cache_status, download_sub_dir)
        if not cache_status & TelewebDownloader.CACHE_DOWNLOADED:
            return None

        return sw_path

    def download_sw(self, sw_path: str, cache_status: int, download_sub_dir: bool = True) -> int:
        # create progress bar
        progress_bar = ProgressBar(100, Print.YELLOW, Print.BLUE, Print.BLACK, False)

        # check cached
        if cache_status & TelewebDownloader.CACHE_DOWNLOADED:
            progress_bar.set_title('Done at %s' % sw_path)
            progress_bar.begin()
            progress_bar.set(100, 100)
            progress_bar.end()
        else:
            def on_progress(path: str, cur: int, total: int):
                nonlocal progress_bar
                nonlocal sw_path
                nonlocal cache_status

                if TelewebServer.PROGRESS_BEGIN == path:
                    progress_bar.set(cur, total, 'Loading file info ...')
                    progress_bar.begin()
                elif TelewebServer.PROGRESS_END == path:
                    progress_bar.set(cur, total, 'Done at %s' % sw_path)
                    progress_bar.end()

                    cache_status |= TelewebDownloader.CACHE_DOWNLOADED
                    TelewebDownloader.set_cache_status(sw_path, cache_status)
                else:
                    progress_bar.set(cur, total, path)

            self.mServer.download_dir(self.DATA_PATH, None, download_sub_dir, on_progress)

        # return status
        return cache_status

    @staticmethod
    def set_cache_status(sw_path: str, status: int = CACHE_INVALID):
        if not os.path.exists(sw_path) or not os.path.isdir(sw_path):
            return

        path = '%s/.cached' % sw_path

        with open(path, 'w') as file:
            file.write(str(status))

    @staticmethod
    def get_cache_status(sw_path: str) -> int:
        if not os.path.exists(sw_path) or not os.path.isdir(sw_path):
            return TelewebDownloader.CACHE_INVALID

        path = '%s/.cached' % sw_path

        if os.path.exists(path):
            with open(path, 'r') as file:
                str_status = file.readline()
                try:
                    return int(str_status)
                except ValueError:
                    return TelewebDownloader.CACHE_INVALID
        else:
            return TelewebDownloader.CACHE_INVALID

    @staticmethod
    def rm_cache(sw_path: str):
        if not os.path.exists(sw_path):
            return

        if os.path.isdir(sw_path):
            os.removedirs(sw_path)
        else:
            os.remove(sw_path)
